home *** CD-ROM | disk | FTP | other *** search
- /* protg.c
- The 'g' protocol.
-
- Copyright (C) 1991, 1992, 1993, 1994, 1995 Ian Lance Taylor
-
- This file is part of the Taylor UUCP package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The author of the program may be contacted at ian@airs.com or
- c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
- */
-
- #include "uucp.h"
-
- #if USE_RCS_ID
- const char protg_rcsid[] = "$Id: protg.c,v 1.68 1995/06/21 19:15:22 ian Rel $";
- #endif
-
- #include <ctype.h>
- #include <errno.h>
-
- #include "uudefs.h"
- #include "uuconf.h"
- #include "conn.h"
- #include "trans.h"
- #include "system.h"
- #include "prot.h"
-
- /* Each 'g' protocol packet begins with six bytes. They are:
-
- <DLE><k><c0><c1><C><x>
-
- <DLE> is the ASCII DLE character (^P or '\020').
- if 1 <= <k> <= 8, the packet is followed by 2 ** (k + 4) bytes of data;
- if <k> == 9, these six bytes are a complete control packet;
- other value of <k> are illegal.
- <c0> is the low byte of a checksum.
- <c1> is the high byte of a checksum.
- <C> is a control byte (see below).
- <x> is <k> ^ <c0> ^ <c1> ^ <C>.
-
- The control byte <C> is divided into three bitfields:
-
- t t x x x y y y
-
- The two bit field tt is the packet type.
- The three bit field xxx is the control type for a control packet, or
- the sequence number for a data packet.
- The three bit field yyy is a value for a control packet, or the
- sequence number of the last packet received for a data packet.
-
- For all successfully recieved packets, the control byte is stored
- into iGpacket_control. */
-
- /* Names for the bytes in the frame header. */
- #define IFRAME_DLE (0)
- #define IFRAME_K (1)
- #define IFRAME_CHECKLOW (2)
- #define IFRAME_CHECKHIGH (3)
- #define IFRAME_CONTROL (4)
- #define IFRAME_XOR (5)
-
- /* Length of the frame header. */
- #define CFRAMELEN (6)
-
- /* Macros to break apart the control bytes. */
- #define CONTROL_TT(b) ((int)(((b) >> 6) & 03))
- #define CONTROL_XXX(b) ((int)(((b) >> 3) & 07))
- #define CONTROL_YYY(b) ((int)((b) & 07))
-
- /* DLE value. */
- #define DLE ('\020')
-
- /* Get the length of a packet given a pointer to the header. */
- #define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4)))
-
- /* <k> field value for a control message. */
- #define KCONTROL (9)
-
- /* Get the next sequence number given a sequence number. */
- #define INEXTSEQ(i) ((i + 1) & 07)
-
- /* Compute i1 - i2 modulo 8. */
- #define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07)
-
- /* Packet types. These are from the tt field.
- CONTROL -- control packet
- ALTCHAN -- alternate channel; not used by UUCP
- DATA -- full data segment
- SHORTDATA -- less than full data segment (all the bytes specified by
- the packet length <k> are always transferred). Let <u> be the number
- of bytes in the data segment not to be used. If <u> <= 0x7f, the first
- byte of the data segment is <u> and the data follows. If <u> > 0x7f,
- the first byte of the data segment is 0x80 | (<u> & 0x7f), the second
- byte of the data segment is <u> >> 7, and the data follows. The
- maximum possible data segment size is 2**12, so this handles all
- possible cases. */
- #define CONTROL (0)
- #define ALTCHAN (1)
- #define DATA (2)
- #define SHORTDATA (3)
-
- /* Control types. These are from the xxx field if the type (tt field)
- is CONTROL.
-
- CLOSE -- close the connection
- RJ -- reject; packet yyy last to be received correctly
- SRJ -- selective reject; reject only packet yyy (not used by UUCP)
- RR -- receiver ready; packet yyy received correctly
- INITC -- third step of initialization; yyy holds window size
- INITB -- second step of initialization; yyy holds maximum <k> value - 1
- INITA -- first step of initialization; yyy holds window size.
-
- The yyy value for RR is the same as the yyy value for an ordinary
- data packet. */
- #define CLOSE (1)
- #define RJ (2)
- #define SRJ (3)
- #define RR (4)
- #define INITC (5)
- #define INITB (6)
- #define INITA (7)
-
- /* Maximum amount of data in a single packet. This is set by the <k>
- field in the header; the amount of data in a packet is
- 2 ** (<k> + 4). <k> ranges from 1 to 8. */
-
- #define CMAXDATAINDEX (8)
-
- #define CMAXDATA (1 << (CMAXDATAINDEX + 4))
-
- /* Maximum window size. */
- #define CMAXWINDOW (7)
-
- /* Defaults for the protocol parameters. These may all be changed by
- using the ``protocol-parameter g'' command, so there is no
- particular reason to change the values given here. */
-
- /* The desired window size. This is what we tell the other system to
- use. It must be between 1 and 7, and there's no reason to use less
- than 7. Protocol parameter ``window''. */
- #define IWINDOW (7)
-
- /* The desired packet size. Many implementations only support 64 byte
- packets. Protocol parameter ``packet-size''. */
- #define IPACKSIZE (64)
-
- /* The number of times to retry the exchange of INIT packets when
- starting the protocol. Protocol parameter ``startup-retries''. */
- #define CSTARTUP_RETRIES (8)
-
- /* The timeout to use when waiting for an INIT packet when starting up
- the protocol. Protocol parameter ``init-timeout''. */
- #define CEXCHANGE_INIT_TIMEOUT (10)
-
- /* The number of times to retry sending and waiting for a single INIT
- packet when starting the protocol. This controls a single INIT
- packet, while CSTARTUP_RETRIES controls how many times to try the
- entire INIT sequence. Protocol parameter ``init-retries''. */
- #define CEXCHANGE_INIT_RETRIES (4)
-
- /* The timeout to use when waiting for a packet. Protocol parameter
- ``timeout''. */
- #define CTIMEOUT (10)
-
- /* The number of times to retry waiting for a packet. Each time the
- timeout fails we send a copy of our last data packet or a reject
- message for the packet we expect from the other side, depending on
- whether we are waiting for an acknowledgement or a data packet.
- This is the number of times we try doing that and then waiting
- again. Protocol parameter ``retries''. */
- #define CRETRIES (6)
-
- /* If we see more than this much unrecognized data, we drop the
- connection. This must be larger than a single packet size, which
- means it must be larger than 4096 (the largest possible packet
- size). Protocol parameter ``garbage''. */
- #define CGARBAGE (10000)
-
- /* If we see more than this many protocol errors, we drop the
- connection. Protocol parameter ``errors''. */
- #define CERRORS (100)
-
- /* Default decay rate. Each time we send or receive this many packets
- succesfully, we decrement the error level by one (protocol
- parameter ``error-decay''). */
- #define CERROR_DECAY (10)
-
- /* If this value is non-zero, it will be used as the remote window
- size regardless of what the other side requested. This can be
- useful for dealing with some particularly flawed packages. This
- default value should always be 0, and protocol parameter
- ``remote-window'' should be used for the affected systems. */
- #define IREMOTE_WINDOW (0)
-
- /* If this value is non-zero, it will be used as the packet size to
- send to the remote system regardless of what it requested. It's
- difficult to imagine any circumstances where you would want to set
- this. Protocol parameter ``remote-packet-size''. */
- #define IREMOTE_PACKSIZE (0)
-
- /* Local variables. */
-
- /* Next sequence number to send. */
- static int iGsendseq;
-
- /* Last sequence number that has been acked. */
- static int iGremote_ack;
-
- /* Last sequence number to be retransmitted. */
- static int iGretransmit_seq;
-
- /* Last sequence number we have received. */
- static int iGrecseq;
-
- /* Last sequence number we have acked. */
- static int iGlocal_ack;
-
- /* Window size to request (protocol parameter ``window''). */
- static int iGrequest_winsize = IWINDOW;
-
- /* Packet size to request (protocol parameter ``packet-size''). */
- static int iGrequest_packsize = IPACKSIZE;
-
- /* Remote window size (set during handshake). */
- static int iGremote_winsize;
-
- /* Forced remote window size (protocol parameter ``remote-window''). */
- static int iGforced_remote_winsize = IREMOTE_WINDOW;
-
- /* Remote segment size (set during handshake). This is one less than
- the value in a packet header. */
- static int iGremote_segsize;
-
- /* Remote packet size (set based on iGremote_segsize). */
- static size_t iGremote_packsize;
-
- /* Forced remote packet size (protocol parameter
- ``remote-packet-size''). */
- static int iGforced_remote_packsize = IREMOTE_PACKSIZE;
-
- /* Recieved control byte. */
- static int iGpacket_control;
-
- /* Number of times to retry the initial handshake. Protocol parameter
- ``startup-retries''. */
- static int cGstartup_retries = CSTARTUP_RETRIES;
-
- /* Number of times to retry sending an initial control packet.
- Protocol parameter ``init-retries''. */
- static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
-
- /* Timeout (seconds) for receiving an initial control packet.
- Protocol parameter ``init-timeout''. */
- static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
-
- /* Timeout (seconds) for receiving a data packet. Protocol parameter
- ``timeout''. */
- static int cGtimeout = CTIMEOUT;
-
- /* Maximum number of timeouts when receiving a data packet or
- acknowledgement. Protocol parameter ``retries''. */
- static int cGretries = CRETRIES;
-
- /* Amount of garbage data we are prepared to see before giving up.
- Protocol parameter ``garbage''. */
- static int cGgarbage_data = CGARBAGE;
-
- /* Maximum number of errors we are prepared to see before giving up.
- Protocol parameter ``errors''. */
- static int cGmax_errors = CERRORS;
-
- /* Each time we receive this many packets succesfully, we decrement
- the error level by one (protocol parameter ``error-decay''). */
- static int cGerror_decay = CERROR_DECAY;
-
- /* Whether to use shorter packets when possible. Protocol parameter
- ``short-packets''. */
- static boolean fGshort_packets = TRUE;
-
- /* Protocol parameter commands. */
- struct uuconf_cmdtab asGproto_params[] =
- {
- { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL },
- { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize,
- NULL },
- { "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries,
- NULL },
- { "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout,
- NULL },
- { "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries,
- NULL },
- { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL },
- { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL },
- { "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL },
- { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL },
- { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL },
- { "remote-window", UUCONF_CMDTABTYPE_INT,
- (pointer) &iGforced_remote_winsize, NULL },
- { "remote-packet-size", UUCONF_CMDTABTYPE_INT,
- (pointer) &iGforced_remote_packsize, NULL },
- { "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets,
- NULL },
- { NULL, 0, NULL, NULL }
- };
-
- /* Statistics. */
-
- /* Number of packets we have sent. */
- static long cGsent_packets;
-
- /* Number of packets we have resent (these are not included in
- cGsent_packets). */
- static long cGresent_packets;
-
- /* Number of packets we have delayed sending (these should not be
- counted in cGresent_packets). */
- static long cGdelayed_packets;
-
- /* Number of packets we have received. */
- static long cGrec_packets;
-
- /* Number of packets rejected because the header was bad. */
- static long cGbad_hdr;
-
- /* Number of packets rejected because the checksum was bad. */
- static long cGbad_checksum;
-
- /* Number of packets received out of order. */
- static long cGbad_order;
-
- /* Number of packets rejected by receiver (number of RJ packets
- received). */
- static long cGremote_rejects;
-
- /* Number of duplicate RR packets treated as RJ packets. Some UUCP
- packages appear to never send RJ packets, but only RR packets. If
- no RJ has been seen, fgprocess_data treats a duplicate RR as an RJ
- and increments this variable. */
- static long cGremote_duprrs;
-
- /* The error level. This is the total number of errors as adjusted by
- cGerror_decay. */
- static long cGerror_level;
-
- /* Each time we send an RJ, we can expect several out of order of
- packets, because the other side will probably have sent a full
- window by the time it sees the RJ. This variable keeps track of
- the number of out of order packets we expect to see. We don't
- count expected out of order packets against the error level. This
- is reset to 0 when an in order packet is received. */
- static int cGexpect_bad_order;
-
- #if DEBUG > 1
- /* Control packet names used for debugging. */
- static const char * const azGcontrol[] =
- {"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
- #endif
-
- /* Local functions. */
- static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl,
- int ival, int *piset));
- static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl,
- int ival));
- static char *zgadjust_ack P((int iseq));
- static boolean fgwait_for_packet P((struct sdaemon *qdaemon,
- boolean freturncontrol, int ctimeout,
- int cretries));
- static boolean fgsend_acks P((struct sdaemon *qdaemon));
- static boolean fggot_ack P((struct sdaemon *qdaemon, int iack));
- static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks,
- boolean freturncontrol,
- boolean *pfexit, size_t *pcneed,
- boolean *pffound));
- static boolean fginit_sendbuffers P((boolean fallocate));
- static boolean fgcheck_errors P((struct sdaemon *qdaemon));
- static int igchecksum P((const char *zdata, size_t clen));
- static int igchecksum2 P((const char *zfirst, size_t cfirst,
- const char *zsecond, size_t csecond));
-
- /* Start the protocol. This requires a three way handshake. Both sides
- must send and receive an INITA packet, an INITB packet, and an INITC
- packet. The INITA and INITC packets contain the window size, and the
- INITB packet contains the packet size. */
-
- boolean
- fgstart (qdaemon, pzlog)
- struct sdaemon *qdaemon;
- char **pzlog;
- {
- int iseg;
- int i;
- boolean fgota, fgotb;
-
- *pzlog = NULL;
-
- /* The 'g' protocol requires a full eight bit interface. */
- if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
- STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
- return FALSE;
-
- iGsendseq = 1;
- iGremote_ack = 0;
- iGretransmit_seq = -1;
- iGrecseq = 0;
- iGlocal_ack = 0;
- cGsent_packets = 0;
- cGresent_packets = 0;
- cGdelayed_packets = 0;
- cGrec_packets = 0;
- cGbad_hdr = 0;
- cGbad_checksum = 0;
- cGbad_order = 0;
- cGremote_rejects = 0;
- cGremote_duprrs = 0;
- cGerror_level = 0;
- cGexpect_bad_order = 0;
-
- /* We must determine the segment size based on the packet size
- which may have been modified by a protocol parameter command.
- A segment size of 2^n is passed as n - 5. */
- i = iGrequest_packsize;
- iseg = -1;
- while (i > 0)
- {
- ++iseg;
- i >>= 1;
- }
- iseg -= 5;
- if (iseg < 0 || iseg > 7)
- {
- ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol",
- iGrequest_packsize, qdaemon->qproto->bname);
- iseg = 1;
- }
-
- if (iGrequest_winsize <= 0 || iGrequest_winsize > 7)
- {
- ulog (LOG_ERROR, "Illegal window size %d for '%c' protocol",
- iGrequest_winsize, qdaemon->qproto->bname);
- iGrequest_winsize = IWINDOW;
- }
-
- fgota = FALSE;
- fgotb = FALSE;
- for (i = 0; i < cGstartup_retries; i++)
- {
- if (fgota)
- {
- if (! fgsend_control (qdaemon, INITA, iGrequest_winsize))
- return FALSE;
- }
- else
- {
- if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize,
- &iGremote_winsize))
- continue;
- }
- fgota = TRUE;
-
- if (fgotb)
- {
- if (! fgsend_control (qdaemon, INITB, iseg))
- return FALSE;
- }
- else
- {
- if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize))
- continue;
- }
- fgotb = TRUE;
-
- if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize,
- &iGremote_winsize))
- continue;
-
- /* We have succesfully connected. Determine the remote packet
- size. */
- iGremote_packsize = 1 << (iGremote_segsize + 5);
-
- /* If the user requested us to force specific remote window and
- packet sizes, do so now. */
- if (iGforced_remote_winsize > 0
- && iGforced_remote_winsize <= CMAXWINDOW)
- iGremote_winsize = iGforced_remote_winsize;
-
- if (iGforced_remote_packsize >= 32
- && iGforced_remote_packsize <= 4096)
- {
- /* Force the value to a power of two. */
- i = iGforced_remote_packsize;
- iseg = -1;
- while (i > 0)
- {
- ++iseg;
- i >>= 1;
- }
- iGremote_packsize = 1 << iseg;
- iGremote_segsize = iseg - 5;
- }
-
- /* Set up packet buffers to use. We don't do this until we know
- the maximum packet size we are going to send. */
- if (! fginit_sendbuffers (TRUE))
- return FALSE;
-
- *pzlog =
- zbufalc (sizeof "protocol '' sending packet/window / receiving /"
- + 64);
- sprintf (*pzlog,
- "protocol '%c' sending packet/window %d/%d receiving %d/%d",
- qdaemon->qproto->bname, (int) iGremote_packsize,
- (int) iGremote_winsize, (int) iGrequest_packsize,
- (int) iGrequest_winsize);
-
- return TRUE;
- }
-
- DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgstart: Protocol startup failed");
-
- return FALSE;
- }
-
- /* The 'G' protocol is identical to the 'g' protocol, except that
- short packets are never supported. */
-
- boolean
- fbiggstart (qdaemon, pzlog)
- struct sdaemon *qdaemon;
- char **pzlog;
- {
- fGshort_packets = FALSE;
- return fgstart (qdaemon, pzlog);
- }
-
- /* The 'v' protocol is identical to the 'g' protocol, except that the
- packet size defaults to 512 bytes. Rather than really get it
- right, we automatically switch from the usual default of 64 to 512.
- This won't work correctly if somebody does protocol-parameter v
- packet-size 64. */
-
- boolean
- fvstart (qdaemon, pzlog)
- struct sdaemon *qdaemon;
- char **pzlog;
- {
- if (iGrequest_packsize == IPACKSIZE)
- iGrequest_packsize = 1024;
- return fgstart (qdaemon, pzlog);
- }
-
- /* Exchange initialization messages with the other system.
-
- A problem:
-
- We send INITA; it gets received
- We receive INITA
- We send INITB; it gets garbled
- We receive INITB
-
- We have seen and sent INITB, so we start to send INITC. The other
- side as sent INITB but not seen it, so it times out and resends
- INITB. We will continue sending INITC and the other side will
- continue sending INITB until both sides give up and start again
- with INITA.
-
- It might seem as though if we are sending INITC and receive INITB,
- we should resend our INITB, but this could cause infinite echoing
- of INITB on a long-latency line. Rather than risk that, I have
- implemented a fast drop-back procedure. If we are sending INITB and
- receive INITC, the other side has gotten ahead of us. We immediately
- fail and begin again with INITA. For the other side, if we are
- sending INITC and see INITA, we also immediately fail back to INITA.
-
- Unfortunately, this doesn't work for the other case, in which we
- are sending INITB but the other side has not yet seen INITA. As
- far as I can see, if this happens we just have to wait until we
- time out and resend INITA. */
-
- static boolean
- fgexchange_init (qdaemon, ictl, ival, piset)
- struct sdaemon *qdaemon;
- int ictl;
- int ival;
- int *piset;
- {
- int i;
-
- /* The three-way handshake should be independent of who initializes
- it, but it seems that some versions of uucico assume that the
- caller sends first and the callee responds. This only matters if
- we are the callee and the first packet is garbled. If we send a
- packet, the other side will assume that we must have seen the
- packet they sent and will never time out and send it again.
- Therefore, if we are the callee we don't send a packet the first
- time through the loop. This can still fail, but should usually
- work, and, after all, if the initialization packets are received
- correctly there will be no problem no matter what we do. */
- for (i = 0; i < cGexchange_init_retries; i++)
- {
- long itime;
- int ctimeout;
-
- if (qdaemon->fcaller || i > 0)
- {
- if (! fgsend_control (qdaemon, ictl, ival))
- return FALSE;
- }
-
- itime = ixsysdep_time ((long *) NULL);
- ctimeout = cGexchange_init_timeout;
-
- do
- {
- long inewtime;
-
- /* We pass 0 as the retry count to fgwait_for_packet because
- we want to handle retries here and because if it retried
- it would send a packet, which would be bad. */
- if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0))
- break;
-
- if (CONTROL_TT (iGpacket_control) == CONTROL)
- {
- if (CONTROL_XXX (iGpacket_control) == ictl)
- {
- *piset = CONTROL_YYY (iGpacket_control);
-
- /* If we didn't already send our initialization
- packet, send it now. */
- if (! qdaemon->fcaller && i == 0)
- {
- if (! fgsend_control (qdaemon, ictl, ival))
- return FALSE;
- }
-
- return TRUE;
- }
-
- /* If the other side is farther along than we are,
- we have lost a packet. Fail immediately back to
- INITA (but don't fail if we are already doing INITA,
- since that would count against cStart_retries more
- than it should). */
- if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA)
- return FALSE;
-
- /* If we are sending INITC and we receive an INITA, the other
- side has failed back (we know this because we have
- seen an INITB from them). Fail back ourselves to
- start the whole handshake over again. */
- if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC)
- return FALSE;
-
- /* As a special hack, if we are sending INITC and we
- receive INITB, we update the segment size from the
- packet. This permits a second INITB to override the
- first one. It would be nice to do this in a cleaner
- way. */
- if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC)
- iGremote_segsize = CONTROL_YYY (iGpacket_control);
- }
-
- inewtime = ixsysdep_time ((long *) NULL);
- ctimeout -= inewtime - itime;
- }
- while (ctimeout > 0);
- }
-
- return FALSE;
- }
-
- /* Shut down the protocol. */
-
- boolean
- fgshutdown (qdaemon)
- struct sdaemon *qdaemon;
- {
- (void) fgsend_control (qdaemon, CLOSE, 0);
- (void) fgsend_control (qdaemon, CLOSE, 0);
- (void) fginit_sendbuffers (FALSE);
-
- /* The count of sent packets may not be accurate, because some of
- them may have not been sent yet if the connection failed in the
- middle (the ones that counted for cGdelayed_packets). I don't
- think it's worth being precise. */
- ulog (LOG_NORMAL,
- "Protocol '%c' packets: sent %ld, resent %ld, received %ld",
- qdaemon->qproto->bname, cGsent_packets,
- cGresent_packets - cGdelayed_packets, cGrec_packets);
- if (cGbad_hdr != 0
- || cGbad_checksum != 0
- || cGbad_order != 0
- || cGremote_rejects != 0
- || cGremote_duprrs != 0)
- ulog (LOG_NORMAL,
- "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
- cGbad_hdr, cGbad_checksum, cGbad_order,
- cGremote_duprrs + cGremote_rejects);
-
- /* Reset all the parameters to their default values, so that the
- protocol parameters used for this connection do not affect the
- next one. */
- iGrequest_winsize = IWINDOW;
- iGrequest_packsize = IPACKSIZE;
- cGstartup_retries = CSTARTUP_RETRIES;
- cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
- cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
- cGtimeout = CTIMEOUT;
- cGretries = CRETRIES;
- cGgarbage_data = CGARBAGE;
- cGmax_errors = CERRORS;
- cGerror_decay = CERROR_DECAY;
- iGforced_remote_winsize = IREMOTE_WINDOW;
- iGforced_remote_packsize = IREMOTE_PACKSIZE;
- fGshort_packets = TRUE;
-
- return TRUE;
- }
-
- /* Send a command string. We send packets containing the string until
- the entire string has been sent. Each packet is full. */
-
- /*ARGSUSED*/
- boolean
- fgsendcmd (qdaemon, z, ilocal, iremote)
- struct sdaemon *qdaemon;
- const char *z;
- int ilocal;
- int iremote;
- {
- size_t clen;
- boolean fagain;
-
- DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z);
-
- clen = strlen (z);
-
- do
- {
- char *zpacket;
- size_t cdummy;
-
- zpacket = zggetspace (qdaemon, &cdummy);
-
- if (clen < iGremote_packsize)
- {
- size_t csize;
-
- /* If the remote packet size is larger than 64 (the default,
- which may indicate an older UUCP package), try to fit
- this command into a smaller packet. We still always send
- a complete packet, though. */
- if (iGremote_packsize <= 64 || ! fGshort_packets)
- csize = iGremote_packsize;
- else
- {
- csize = 32;
- while (csize <= clen)
- csize <<= 1;
- }
-
- memcpy (zpacket, z, clen);
- if (csize > clen)
- bzero (zpacket + clen, csize - clen);
- fagain = FALSE;
-
- if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0))
- return FALSE;
- }
- else
- {
- memcpy (zpacket, z, iGremote_packsize);
- z += iGremote_packsize;
- clen -= iGremote_packsize;
- fagain = TRUE;
-
- if (! fgsenddata (qdaemon, zpacket, iGremote_packsize,
- 0, 0, (long) 0))
- return FALSE;
- }
- }
- while (fagain);
-
- return TRUE;
- }
-
- /* We keep an array of buffers to retransmit as necessary. Rather
- than waste static space on large buffer sizes, we allocate the
- buffers once we know how large the other system expects them to be.
- The sequence numbers used in the 'g' protocol are only three bits
- long, so we allocate eight buffers and maintain a correspondence
- between buffer index and sequence number. This always wastes some
- buffer space, but it's easy to implement.
-
- We leave room at the front of the buffer for the frame header and
- two additional bytes. The two extra bytes are used for short
- packets, which essentially use a longer header and shorter data.
- We do this to avoid moving the data. We zero out any unused bytes
- before the frame, so we can locate the real header given a buffer
- by finding the first non-zero byte (which will be one of the first
- three bytes in the buffer). */
-
- #define CSENDBUFFERS (CMAXWINDOW + 1)
-
- static char *azGsendbuffers[CSENDBUFFERS];
-
- static boolean
- fginit_sendbuffers (fallocate)
- boolean fallocate;
- {
- int i;
-
- /* Free up any remaining old buffers. */
- for (i = 0; i < CSENDBUFFERS; i++)
- {
- xfree ((pointer) azGsendbuffers[i]);
- if (fallocate)
- {
- azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2
- + iGremote_packsize);
- if (azGsendbuffers[i] == NULL)
- return FALSE;
-
- /* This bzero might not seem necessary, since before we send
- out each packet we zero out any non-data bytes. However,
- if we receive an SRJ at the start of the conversation, we
- will send out the packet before it has been set to
- anything, thus sending the contents of our heap. We
- avoid this by using bzero. */
- bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize);
- }
- else
- azGsendbuffers[i] = NULL;
- }
- return TRUE;
- }
-
- /* Allocate a packet to send out. The return value of this function
- must be filled in and passed to fgsenddata, or discarded. This
- will ensure that the buffers and iGsendseq stay in synch. Set
- *pclen to the amount of data to place in the buffer. */
-
- /*ARGSUSED*/
- char *
- zggetspace (qdaemon, pclen)
- struct sdaemon *qdaemon;
- size_t *pclen;
- {
- *pclen = iGremote_packsize;
- return azGsendbuffers[iGsendseq] + CFRAMELEN + 2;
- }
-
- /* Send out a data packet. This computes the checksum, sets up the
- header, and sends the packet out. The argument zdata should point
- to the return value of zggetspace. */
-
- /*ARGSIGNORED*/
- boolean
- fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
- struct sdaemon *qdaemon;
- char *zdata;
- size_t cdata;
- int ilocal;
- int iremote;
- long ipos;
- {
- char *z;
- int itt, iseg;
- size_t csize;
- int iclr1, iclr2;
- unsigned short icheck;
-
- /* Set the initial length bytes. See the description at the definition
- of SHORTDATA, above. */
- itt = DATA;
- csize = iGremote_packsize;
- iseg = iGremote_segsize + 1;
-
- #if DEBUG > 0
- if (cdata > csize)
- ulog (LOG_FATAL, "fgsend_packet: Packet size too large");
- #endif
-
- iclr1 = -1;
- iclr2 = -2;
- if (cdata < csize)
- {
- /* If the remote packet size is larger than 64, the default, we
- can assume they can handle a smaller packet as well, which
- will be more efficient to send. */
- if (iGremote_packsize > 64 && fGshort_packets)
- {
- /* The packet size is 1 << (iseg + 4). */
- iseg = 1;
- csize = 32;
- while (csize < cdata)
- {
- csize <<= 1;
- ++iseg;
- }
- }
-
- if (csize != cdata)
- {
- size_t cshort;
-
- /* We have to add bytes which indicate how short the packet
- is. We do this by pushing the header backward, which we
- can do because we allocated two extra bytes for this
- purpose. */
- iclr2 = 0;
- itt = SHORTDATA;
- cshort = csize - cdata;
- if (cshort <= 127)
- {
- --zdata;
- zdata[0] = (char) cshort;
- zdata[-1] = '\0';
- if (cshort > 1)
- bzero (zdata + cdata + 1, cshort - 1);
- }
- else
- {
- zdata -= 2;
- zdata[0] = (char) (0x80 | (cshort & 0x7f));
- zdata[1] = (char) (cshort >> 7);
- bzero (zdata + cdata + 2, cshort - 2);
- iclr1 = 0;
- }
- }
- }
-
- z = zdata - CFRAMELEN;
-
- /* Zero out the preceding bytes, in case the last time this buffer
- was used those bytes were used. We need to zero out the initial
- bytes so that we can find the true start of the packet in
- zgadjust_ack. */
- z[iclr1] = '\0';
- z[iclr2] = '\0';
-
- z[IFRAME_DLE] = DLE;
- z[IFRAME_K] = (char) iseg;
-
- icheck = (unsigned short) igchecksum (zdata, csize);
-
- /* We're just about ready to go. Wait until there is room in the
- receiver's window for us to send the packet. We do this now so
- that we send the correct value for the last packet received.
- Note that if iGsendseq == iGremote_ack, this means that the
- sequence numbers are actually 8 apart, since the packet could not
- have been acknowledged before it was sent; this can happen when
- the window size is 7. */
- while (iGsendseq == iGremote_ack
- || CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize)
- {
- if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries))
- return FALSE;
- }
-
- /* Ack all packets up to the next one, since the UUCP protocol
- requires that all packets be acked in order. */
- while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1)
- {
- iGlocal_ack = INEXTSEQ (iGlocal_ack);
- if (! fgsend_control (qdaemon, RR, iGlocal_ack))
- return FALSE;
- }
- iGlocal_ack = iGrecseq;
-
- z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq);
-
- iGsendseq = INEXTSEQ (iGsendseq);
-
- icheck = ((unsigned short)
- ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
- z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
- z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
-
- z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
- ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
-
- /* If we're waiting for acks of retransmitted packets, then don't
- send this packet yet. The other side may not be ready for it
- yet. Instead, code in fggot_ack will send the outstanding
- packets when an ack is received. */
- ++cGsent_packets;
-
- if (iGretransmit_seq != -1)
- {
- ++cGdelayed_packets;
- return TRUE;
- }
-
- DEBUG_MESSAGE2 (DEBUG_PROTO,
- "fgsenddata: Sending packet %d (%d bytes)",
- CONTROL_XXX (z[IFRAME_CONTROL]), cdata);
-
- return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE);
- }
-
- /* Recompute the control byte and checksum of a packet so that it
- includes the correct packet acknowledgement. This is called when a
- packet is retransmitted to make sure the retransmission does not
- confuse the other side. It returns a pointer to the start of the
- packet, skipping the bytes that may be unused at the start of
- azGsendbuffers[iseq]. */
-
- static char *
- zgadjust_ack (iseq)
- int iseq;
- {
- register char *z;
- unsigned short icheck;
-
- z = azGsendbuffers[iseq];
- if (*z == '\0')
- ++z;
- if (*z == '\0')
- ++z;
-
- /* If the received packet number is the same, there is nothing
- to do. */
- if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq)
- return z;
-
- /* Get the old checksum. */
- icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8)
- | (z[IFRAME_CHECKLOW] & 0xff));
- icheck = ((unsigned short)
- (((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff));
-
- /* Update the control byte. */
- z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq);
-
- /* Create the new checksum. */
- icheck = ((unsigned short)
- ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
- z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
- z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
-
- /* Update the XOR byte. */
- z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
- ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
-
- return z;
- }
-
- /* Send a control packet. These are fairly simple to construct. It
- seems reasonable to me that we should be able to send a control
- packet at any time, even if the receive window is closed. In
- particular, we don't want to delay when sending a CLOSE control
- message. If I'm wrong, it can be changed easily enough. */
-
- static boolean
- fgsend_control (qdaemon, ixxx, iyyy)
- struct sdaemon *qdaemon;
- int ixxx;
- int iyyy;
- {
- char ab[CFRAMELEN];
- int ictl;
- unsigned short icheck;
-
- #if DEBUG > 1
- if (FDEBUGGING (DEBUG_PROTO) ||
- (FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR))
- ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d",
- azGcontrol[ixxx], iyyy);
- #endif
-
- ab[IFRAME_DLE] = DLE;
- ab[IFRAME_K] = KCONTROL;
-
- ictl = (CONTROL << 6) | (ixxx << 3) | iyyy;
- icheck = (unsigned short) (0xaaaa - ictl);
- ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
- ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
-
- ab[IFRAME_CONTROL] = (char) ictl;
-
- ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
- ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]);
-
- return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE);
- }
-
- /* Wait for data to come in. This continues processing until a
- complete file or command has been received. */
-
- boolean
- fgwait (qdaemon)
- struct sdaemon *qdaemon;
- {
- return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries);
- }
-
- /* Get a packet. This is called when we have nothing to send, but
- want to wait for a packet to come in. If freturncontrol is TRUE,
- this will return after getting any control packet. Otherwise, it
- will continue to receive packets until a complete file or a
- complete command has been received. The timeout and the number of
- retries are specified as arguments. The function returns FALSE if
- an error occurs or if cretries timeouts of ctimeout seconds were
- exceeded. */
-
- static boolean
- fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries)
- struct sdaemon *qdaemon;
- boolean freturncontrol;
- int ctimeout;
- int cretries;
- {
- int ctimeouts;
- int cgarbage;
- int cshort;
-
- ctimeouts = 0;
- cgarbage = 0;
- cshort = 0;
-
- while (TRUE)
- {
- boolean fexit;
- size_t cneed;
- boolean ffound;
- size_t crec;
-
- if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit,
- &cneed, &ffound))
- return FALSE;
-
- if (fexit)
- return TRUE;
-
- DEBUG_MESSAGE1 (DEBUG_PROTO,
- "fgwait_for_packet: Need %lu bytes",
- (unsigned long) cneed);
-
- if (ffound)
- {
- ctimeouts = 0;
- cgarbage = 0;
- }
- else
- {
- if (cgarbage > cGgarbage_data)
- {
- ulog (LOG_ERROR, "Too much unrecognized data");
- return FALSE;
- }
- }
-
- if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
- return FALSE;
-
- cgarbage += crec;
-
- if (crec != 0)
- {
- /* If we don't get enough data twice in a row, we may have
- dropped some data and still be looking for the end of a
- large packet. Incrementing iPrecstart will force
- fgprocess_data to skip that packet and look through the
- rest of the data. In some situations, this will be a
- mistake. */
- if (crec >= cneed)
- cshort = 0;
- else
- {
- ++cshort;
- if (cshort > 1)
- {
- iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
- cshort = 0;
- }
- }
- }
- else
- {
- /* The read timed out. If we have an unacknowledged packet,
- send it again. Otherwise, send an RJ with the last
- packet we received correctly. */
- ++ctimeouts;
- if (ctimeouts > cretries)
- {
- if (cretries > 0)
- ulog (LOG_ERROR, "Timed out waiting for packet");
- return FALSE;
- }
-
- if (INEXTSEQ (iGremote_ack) != iGsendseq)
- {
- int inext;
- char *zsend;
-
- inext = INEXTSEQ (iGremote_ack);
-
- DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgwait_for_packet: Resending packet %d",
- inext);
-
- ++cGresent_packets;
- zsend = zgadjust_ack (inext);
- if (! fsend_data (qdaemon->qconn, zsend,
- CFRAMELEN + CPACKLEN (zsend), TRUE))
- return FALSE;
- iGretransmit_seq = inext;
- }
- else
- {
- /* Send all pending acks first, to avoid confusing
- the other side. */
- if (iGlocal_ack != iGrecseq)
- {
- if (! fgsend_acks (qdaemon))
- return FALSE;
- }
- if (! fgsend_control (qdaemon, RJ, iGrecseq))
- return FALSE;
- }
- }
- }
- }
-
- /* Send acks for all packets we haven't acked yet. */
-
- static boolean
- fgsend_acks (qdaemon)
- struct sdaemon *qdaemon;
- {
- while (iGlocal_ack != iGrecseq)
- {
- iGlocal_ack = INEXTSEQ (iGlocal_ack);
- if (! fgsend_control (qdaemon, RR, iGlocal_ack))
- return FALSE;
- }
- return TRUE;
- }
-
- /* Handle an ack of a packet. According to Hanrahan's paper, this
- acknowledges all previous packets. If this is an ack for a
- retransmitted packet, continue by resending up to two more packets
- following the retransmitted one. This should recover quickly from
- a line glitch, while avoiding the problem of continual
- retransmission. */
-
- static boolean
- fggot_ack (qdaemon, iack)
- struct sdaemon *qdaemon;
- int iack;
- {
- int inext;
- char *zsend;
-
- /* We only decrement the error level if we are not retransmitting
- packets. We want to catch a sudden downgrade in line quality as
- fast as possible. */
- if (cGerror_level > 0
- && iGretransmit_seq == -1
- && cGsent_packets % cGerror_decay == 0)
- --cGerror_level;
- cGexpect_bad_order = 0;
-
- /* Each time packet 0 is acknowledged, we call uwindow_acked since a
- new window has been acked. */
- if (iack < iGremote_ack)
- uwindow_acked (qdaemon, FALSE);
-
- iGremote_ack = iack;
-
- if (iGretransmit_seq == -1)
- return TRUE;
-
- inext = INEXTSEQ (iGretransmit_seq);
- if (inext == iGsendseq)
- iGretransmit_seq = -1;
- else
- {
- DEBUG_MESSAGE1 (DEBUG_PROTO,
- "fggot_ack: Sending packet %d", inext);
-
- ++cGresent_packets;
- zsend = zgadjust_ack (inext);
- if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend),
- TRUE))
- return FALSE;
- inext = INEXTSEQ (inext);
- if (inext == iGsendseq)
- iGretransmit_seq = -1;
- else
- {
- DEBUG_MESSAGE1 (DEBUG_PROTO,
- "fggot_ack: Sending packet %d", inext);
-
- ++cGresent_packets;
- zsend = zgadjust_ack (inext);
- if (! fsend_data (qdaemon->qconn, zsend,
- CFRAMELEN + CPACKLEN (zsend), TRUE))
- return FALSE;
- iGretransmit_seq = inext;
- }
- }
-
- return TRUE;
- }
-
- /* See if we've received more than the permitted number of errors. If
- we receive a bad packet, we can expect a window full (less one) of
- out of order packets to follow, so we discount cGbad_order
- accordingly. */
-
- static boolean
- fgcheck_errors (qdaemon)
- struct sdaemon *qdaemon;
- {
- if (cGerror_level > cGmax_errors && cGmax_errors >= 0)
- {
- ulog (LOG_ERROR, "Too many '%c' protocol errors",
- qdaemon->qproto->bname);
- return FALSE;
- }
-
- return TRUE;
- }
-
- /* Process the receive buffer into a data packet, if possible. All
- control packets are handled here. When a data packet is received,
- fgprocess_data calls fgot_data with the data; if that sets its
- pfexit argument to TRUE fgprocess_data will set *pfexit to TRUE and
- return TRUE. Also, if the freturncontrol argument is TRUE
- fgprocess_data will set *pfexit to TRUE and return TRUE. Otherwise
- fgprocess_data will continue trying to process data. If some error
- occurs, fgprocess_data will return FALSE. If there is not enough
- data to form a complete packet, then *pfexit will be set to FALSE,
- *pcneed will be set to the number of bytes needed to form a
- complete packet (unless pcneed is NULL) and fgprocess_data will
- return TRUE. If this function found a data packet, and pffound is
- not NULL, it will set *pffound to TRUE; this can be used to tell
- valid data from an endless stream of garbage and control packets.
- If fdoacks is TRUE, received packets will be acknowledged;
- otherwise they must be acknowledged later. */
-
- static boolean
- fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound)
- struct sdaemon *qdaemon;
- boolean fdoacks;
- boolean freturncontrol;
- boolean *pfexit;
- size_t *pcneed;
- boolean *pffound;
- {
- *pfexit = FALSE;
- if (pffound != NULL)
- *pffound = FALSE;
-
- while (iPrecstart != iPrecend)
- {
- char ab[CFRAMELEN];
- int i, iget, cwant;
- unsigned short ihdrcheck, idatcheck;
- const char *zfirst, *zsecond;
- int cfirst, csecond;
- boolean fduprr;
-
- /* Look for the DLE which must start a packet. */
- if (abPrecbuf[iPrecstart] != DLE)
- {
- char *zdle;
-
- cfirst = iPrecend - iPrecstart;
- if (cfirst < 0)
- cfirst = CRECBUFLEN - iPrecstart;
-
- zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst);
-
- if (zdle == NULL)
- {
- iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN;
- continue;
- }
-
- /* We don't need % CRECBUFLEN here because zdle - (abPrecbuf
- + iPrecstart) < cfirst <= CRECBUFLEN - iPrecstart. */
- iPrecstart += zdle - (abPrecbuf + iPrecstart);
- }
-
- /* Get the first six bytes into ab. */
- for (i = 0, iget = iPrecstart;
- i < CFRAMELEN && iget != iPrecend;
- i++, iget = (iget + 1) % CRECBUFLEN)
- ab[i] = abPrecbuf[iget];
-
- /* If there aren't six bytes, there is no packet. */
- if (i < CFRAMELEN)
- {
- if (pcneed != NULL)
- *pcneed = CFRAMELEN - i;
- return TRUE;
- }
-
- /* Make sure these six bytes start a packet. The check on
- IFRAME_DLE is basically a debugging check, since the above
- code should have ensured that it will never fail. If this is
- not the start of a packet, bump iPrecstart and loop around to
- look for another DLE. */
- if (ab[IFRAME_DLE] != DLE
- || ab[IFRAME_K] < 1
- || ab[IFRAME_K] > 9
- || ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
- ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL])
- || CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN)
- {
- ++cGbad_hdr;
- ++cGerror_level;
-
- DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d",
- ab[IFRAME_K] & 0xff,
- CONTROL_TT (ab[IFRAME_CONTROL]),
- ab[IFRAME_XOR] & 0xff,
- (ab[IFRAME_K]
- ^ ab[IFRAME_CHECKLOW]
- ^ ab[IFRAME_CHECKHIGH]
- ^ ab[IFRAME_CONTROL]) & 0xff);
-
- if (! fgcheck_errors (qdaemon))
- return FALSE;
-
- iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
- continue;
- }
-
- /* The zfirst and cfirst pair point to the first set of data for
- this packet; the zsecond and csecond point to the second set,
- in case the packet wraps around the end of the buffer. */
- zfirst = abPrecbuf + iPrecstart + CFRAMELEN;
- cfirst = 0;
- zsecond = NULL;
- csecond = 0;
-
- if (ab[IFRAME_K] == KCONTROL)
- {
- /* This is a control packet. It should not have any data. */
- if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
- {
- ++cGbad_hdr;
- ++cGerror_level;
-
- DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Bad header: control packet with data");
-
- if (! fgcheck_errors (qdaemon))
- return FALSE;
-
- iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
- continue;
- }
-
- idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]);
- cwant = 0;
- }
- else
- {
- int cinbuf;
- unsigned short icheck;
-
- /* This is a data packet. It should not be type CONTROL. */
- if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL)
- {
- ++cGbad_hdr;
- ++cGerror_level;
-
- DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Bad header: data packet is type CONTROL");
-
- if (! fgcheck_errors (qdaemon))
- return FALSE;
-
- iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
- continue;
- }
-
- cinbuf = iPrecend - iPrecstart;
- if (cinbuf < 0)
- cinbuf += CRECBUFLEN;
- cinbuf -= CFRAMELEN;
-
- /* Make sure we have enough data. If we don't, wait for
- more. */
-
- cwant = (int) CPACKLEN (ab);
- if (cinbuf < cwant)
- {
- if (pcneed != NULL)
- *pcneed = cwant - cinbuf;
- return TRUE;
- }
-
- /* Set up the data pointers and compute the checksum. */
- if (iPrecend >= iPrecstart)
- cfirst = cwant;
- else
- {
- cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN);
- if (cfirst >= cwant)
- cfirst = cwant;
- else if (cfirst > 0)
- {
- zsecond = abPrecbuf;
- csecond = cwant - cfirst;
- }
- else
- {
- /* Here cfirst is non-positive, so subtracting from
- abPrecbuf will actually skip the appropriate number
- of bytes at the start of abPrecbuf. */
- zfirst = abPrecbuf - cfirst;
- cfirst = cwant;
- }
- }
-
- if (csecond == 0)
- icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst);
- else
- icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst,
- zsecond,
- (size_t) csecond);
-
- idatcheck = ((unsigned short)
- (((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff)))
- & 0xffff)));
- }
-
- ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8)
- | (ab[IFRAME_CHECKLOW] & 0xff));
-
- if (ihdrcheck != idatcheck)
- {
- DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Bad checksum: header 0x%x, data 0x%x",
- ihdrcheck, idatcheck);
-
- ++cGbad_checksum;
- ++cGerror_level;
-
- if (! fgcheck_errors (qdaemon))
- return FALSE;
-
- /* If the checksum failed for a data packet, then if it was
- the one we were expecting send an RJ, otherwise ignore
- it. Previously if this code got the wrong packet number
- it would send an RR, but that may confuse some Telebit
- modems and it doesn't help in any case since the receiver
- will probably just ignore the RR as a duplicate (that's
- basically what this code does). If we totally missed the
- packet we will time out and send an RJ in the function
- fgwait_for_packet above. */
- if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
- {
- /* Make sure we've acked everything up to this point. */
- if (iGrecseq != iGlocal_ack)
- {
- if (! fgsend_acks (qdaemon))
- return FALSE;
- }
-
- /* If this is the packet we wanted, tell the sender that
- it failed. */
- if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
- {
- if (! fgsend_control (qdaemon, RJ, iGrecseq))
- return FALSE;
- cGexpect_bad_order += iGrequest_winsize - 1;
- }
- }
-
- /* We can't skip the packet data after this, because if we
- have lost incoming bytes the next DLE will be somewhere
- in what we thought was the packet data. */
- iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
- continue;
- }
-
- /* We have a packet; remove the processed bytes from the receive
- buffer. */
- iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN;
-
- /* Store the control byte for the handshake routines. */
- iGpacket_control = ab[IFRAME_CONTROL] & 0xff;
-
- /* Annoyingly, some UUCP packages appear to send an RR packet
- rather than an RJ packet when they want a packet to be
- resent. If we get a duplicate RR and we've never seen an RJ,
- we treat the RR as an RJ. */
- fduprr = FALSE;
- if (cGremote_rejects == 0
- && CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL
- && CONTROL_XXX (ab[IFRAME_CONTROL]) == RR
- && iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL])
- && INEXTSEQ (iGremote_ack) != iGsendseq
- && iGretransmit_seq == -1)
- {
- DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Treating duplicate RR as RJ");
- fduprr = TRUE;
- }
-
- /* Update the received sequence number from the yyy field of a
- data packet (if it is the one we are expecting) or an RR
- control packet. If we've been delaying sending packets until
- we received an ack, this may send out some packets. */
- if ((CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL
- && CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
- || (CONTROL_XXX (ab[IFRAME_CONTROL]) == RR && ! fduprr))
- {
- if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL])))
- return FALSE;
- }
-
- /* If this isn't a control message, make sure we have received
- the expected packet sequence number, acknowledge the packet
- if it's the right one, and process the data. */
- if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
- {
- if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq))
- {
- /* We got the wrong packet number. */
- DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Got packet %d; expected %d",
- CONTROL_XXX (ab[IFRAME_CONTROL]),
- INEXTSEQ (iGrecseq));
-
- if (cGexpect_bad_order > 0)
- --cGexpect_bad_order;
- else
- {
- ++cGbad_order;
- ++cGerror_level;
- if (! fgcheck_errors (qdaemon))
- return FALSE;
- }
-
- /* This code used to send an RR to encourage the other
- side to get back in synch, but that may confuse some
- Telebit modems and does little good in any case,
- since the other side will probably just ignore it
- anyhow (that's what this code does). */
- continue;
- }
-
- /* We got the packet we expected. */
- ++cGrec_packets;
- if (cGerror_level > 0
- && cGrec_packets % cGerror_decay == 0)
- --cGerror_level;
- cGexpect_bad_order = 0;
-
- iGrecseq = INEXTSEQ (iGrecseq);
-
- DEBUG_MESSAGE1 (DEBUG_PROTO,
- "fgprocess_data: Got packet %d", iGrecseq);
-
- /* Tell the caller that we found something. */
- if (pffound != NULL)
- *pffound = TRUE;
-
- /* If we are supposed to do acknowledgements here, send back
- an RR packet. */
- if (fdoacks)
- {
- if (! fgsend_acks (qdaemon))
- return FALSE;
- }
-
- /* If this is a short data packet, adjust the data pointers
- and lengths. */
- if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA)
- {
- int cshort, cmove;
-
- if ((zfirst[0] & 0x80) == 0)
- {
- cshort = zfirst[0] & 0xff;
- cmove = 1;
- }
- else
- {
- int cbyte2;
-
- if (cfirst > 1)
- cbyte2 = zfirst[1] & 0xff;
- else
- cbyte2 = zsecond[0] & 0xff;
- cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7);
- cmove = 2;
- }
-
- DEBUG_MESSAGE1 (DEBUG_PROTO,
- "fgprocess_data: Packet short by %d",
- cshort);
-
- /* Adjust the start of the buffer for the bytes used
- by the count. */
- if (cfirst > cmove)
- {
- zfirst += cmove;
- cfirst -= cmove;
- }
- else
- {
- zfirst = zsecond + (cmove - cfirst);
- cfirst = csecond - (cmove - cfirst);
- csecond = 0;
- }
-
- /* Adjust the length of the buffer for the bytes we are
- not supposed to consider. */
- cshort -= cmove;
- if (csecond >= cshort)
- csecond -= cshort;
- else
- {
- cfirst -= cshort - csecond;
- csecond = 0;
- }
-
- #if DEBUG > 0
- /* This should not happen, but just in case. */
- if (cfirst < 0)
- cfirst = 0;
- #endif
- }
-
- if (! fgot_data (qdaemon, zfirst, (size_t) cfirst,
- zsecond, (size_t) csecond,
- -1, -1, (long) -1,
- INEXTSEQ (iGremote_ack) == iGsendseq,
- pfexit))
- return FALSE;
-
- /* If fgot_data told us that we were finished, get out. */
- if (*pfexit)
- return TRUE;
-
- /* If we've been asked to return control packets, get out
- now. */
- if (freturncontrol)
- {
- *pfexit = TRUE;
- return TRUE;
- }
-
- continue;
- }
-
- /* Handle control messages here. */
- #if DEBUG > 1
- if (FDEBUGGING (DEBUG_PROTO)
- || (FDEBUGGING (DEBUG_ABNORMAL)
- && CONTROL_XXX (ab[IFRAME_CONTROL]) != RR))
- ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d",
- azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])],
- CONTROL_YYY (ab[IFRAME_CONTROL]));
- #endif
-
- switch (CONTROL_XXX (ab[IFRAME_CONTROL]))
- {
- case CLOSE:
- /* The other side has closed the connection. */
- if (fLog_sighup)
- {
- ulog (LOG_ERROR, "Received unexpected CLOSE packet");
- (void) fgsend_control (qdaemon, CLOSE, 0);
- }
- return FALSE;
- case RR:
- /* Acknowledge receipt of a packet. This was already handled
- above, unless we are treating it as RJ. */
- if (! fduprr)
- break;
- /* Fall through. */
- case RJ:
- /* The other side dropped a packet. Begin retransmission with
- the packet following the one acknowledged. We don't
- retransmit the packets immediately, but instead wait
- for the first one to be acked. This prevents us from
- sending an entire window several times if we get several
- RJ packets. */
- iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]);
- iGretransmit_seq = INEXTSEQ (iGremote_ack);
- if (iGretransmit_seq == iGsendseq)
- iGretransmit_seq = -1;
- else
- {
- char *zpack;
-
- DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Remote reject: next %d resending %d",
- iGsendseq, iGretransmit_seq);
-
- ++cGresent_packets;
- if (fduprr)
- ++cGremote_duprrs;
- else
- ++cGremote_rejects;
- ++cGerror_level;
- if (! fgcheck_errors (qdaemon))
- return FALSE;
- zpack = zgadjust_ack (iGretransmit_seq);
- if (! fsend_data (qdaemon->qconn, zpack,
- CFRAMELEN + CPACKLEN (zpack),
- TRUE))
- return FALSE;
- }
- break;
- case SRJ:
- /* Selectively reject a particular packet. This is not used
- by UUCP, but it's easy to support. */
- DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
- "fgprocess_data: Selective reject of %d",
- CONTROL_YYY (ab[IFRAME_CONTROL]));
- {
- char *zpack;
-
- ++cGresent_packets;
- ++cGremote_rejects;
- ++cGerror_level;
- zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL]));
- if (! fsend_data (qdaemon->qconn, zpack,
- CFRAMELEN + CPACKLEN (zpack),
- TRUE))
- return FALSE;
- }
- break;
- case INITC:
- case INITB:
- case INITA:
- /* Ignore attempts to reinitialize. */
- break;
- }
-
- /* If we've been asked to return control packets, get out. */
- if (freturncontrol)
- {
- *pfexit = TRUE;
- return TRUE;
- }
-
- /* Loop around to look for the next packet, if any. */
- }
-
- /* There is no data left in the receive buffer. */
- if (pcneed != NULL)
- *pcneed = CFRAMELEN;
- return TRUE;
- }
-
- /* Compute the 'g' protocol checksum. This is unfortunately rather
- awkward. This is the most time consuming code in the entire
- program. It's also not a great checksum, since it can be fooled
- by some single bit errors. */
-
- /* Sorry about this knavery, but it speeds up the VAX code
- significantly. It would be better to rewrite the whole routine in
- assembler. */
- #ifdef __GNUC__
- #ifdef __vax__
- #define VAX_ASM 1
- #endif
- #endif
-
- #if VAX_ASM
- #define ROTATE(i) \
- asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i))
- #else
- #define ROTATE(i) i += i + ((i & 0x8000) >> 15)
- #endif
-
- #define ITERATION \
- /* Rotate ichk1 left. */ \
- ROTATE (ichk1); \
- \
- /* The guts of the checksum. */ \
- b = BUCHAR (*z++); \
- if (b != 0) \
- { \
- ichk1 &= 0xffff; \
- ichk1 += b; \
- ichk2 += ichk1 ^ c; \
- if ((ichk1 >> 16) != 0) \
- ichk1 ^= ichk2; \
- } \
- else \
- { \
- ichk2 += ichk1 ^ c; \
- ichk1 ^= ichk2; \
- } \
- \
- --c
-
- static int
- igchecksum (z, c)
- register const char *z;
- register size_t c;
- {
- register unsigned long ichk1, ichk2;
-
- ichk1 = 0xffff;
- ichk2 = 0;
-
- do
- {
- register unsigned int b;
-
- ITERATION;
- ITERATION;
- ITERATION;
- ITERATION;
- }
- while (c > 0);
-
- return ichk1 & 0xffff;
- }
-
- /* We use a separate function compute the checksum if the block is
- split around the end of the receive buffer since it occurs much
- less frequently and the checksum is already high up in the
- profiles. These functions are almost identical, and this one
- actually only has a few more instructions in the inner loop. */
-
- static int
- igchecksum2 (zfirst, cfirst, zsecond, csecond)
- const char *zfirst;
- size_t cfirst;
- const char *zsecond;
- size_t csecond;
- {
- register unsigned long ichk1, ichk2;
- register const char *z;
- register size_t c;
-
- z = zfirst;
- c = cfirst + csecond;
-
- ichk1 = 0xffff;
- ichk2 = 0;
-
- do
- {
- register unsigned int b;
-
- ITERATION;
-
- /* If the first buffer has been finished, switch to the second. */
- --cfirst;
- if (cfirst == 0)
- z = zsecond;
- }
- while (c > 0);
-
- return ichk1 & 0xffff;
- }
-